梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)
本教程将从链表的核心概念出发,详细讲解单链表、双向链表、循环链表的定义、实现及核心操作(初始化、插入、删除、遍历、查找),帮助你掌握C++中链表这一重要的数据结构。
链表(Linked List)是一种非连续、非顺序的线性数据结构,由一系列节点(Node)组成,每个节点包含数据域(存储数据)和指针域(存储下一个/上一个节点的地址)。
与数组相比,链表的核心优势是:
核心劣势是:
链表的常见类型:单链表、双向链表、循环链表。
单链表是最简单的链表结构,每个节点仅包含数据域和后继指针域(指向直接后继节点),链表末尾节点的后继指针为nullptr(空指针),链表有一个头节点/头指针指向第一个有效节点。
单链表的特点:
单链表的核心是节点结构体的定义,包含数据域和后继指针域:
#include <iostream>
#include <cstdlib>
using namespace std;
// 定义单链表节点结构体
struct ListNode {
int data; // 数据域:存储整型数据(可根据需求修改类型)
ListNode* next; // 指针域:指向后继节点
// 构造函数:初始化节点
ListNode(int val = 0) : data(val), next(nullptr) {}
};
// 定义单链表类(封装链表操作)
class SinglyLinkedList {
private:
ListNode* head; // 头指针:指向链表第一个节点
int size; // 链表节点个数
public:
// 构造函数:初始化空链表
SinglyLinkedList() : head(nullptr), size(0) {}
// 析构函数:释放链表内存
~SinglyLinkedList() {
ListNode* curr = head;
while (curr != nullptr) {
ListNode* temp = curr;
curr = curr->next;
delete temp;
}
head = nullptr;
size = 0;
}
// 声明核心操作函数(后续实现)
void initList(); // 初始化链表
void createList(int arr[], int n); // 从数组创建链表
bool insertNode(int pos, int val); // 插入节点
bool deleteNode(int pos); // 删除节点
ListNode* findNode(int val); // 查找节点
void traverseList(); // 遍历链表
int getSize() { return size; } // 获取链表长度
};
初始化链表,将头指针置空,节点数置0:
// 初始化空链表
void SinglyLinkedList::initList() {
if (head != nullptr) { // 若链表非空,先释放内存
ListNode* curr = head;
while (curr != nullptr) {
ListNode* temp = curr;
curr = curr->next;
delete temp;
}
}
head = nullptr;
size = 0;
cout << "链表初始化完成!" << endl;
}
尾插法:新节点始终插入到链表末尾,保持数据顺序与数组一致:
// 从数组创建链表(尾插法)
void SinglyLinkedList::createList(int arr[], int n) {
initList(); // 先初始化空链表
if (n <= 0) return;
// 创建头节点
head = new ListNode(arr[0]);
ListNode* tail = head; // 尾指针,指向当前最后一个节点
size = 1;
// 遍历数组,逐个插入节点
for (int i = 1; i < n; i++) {
ListNode* newNode = new ListNode(arr[i]);
tail->next = newNode; // 尾节点指向新节点
tail = newNode; // 尾指针后移
size++;
}
cout << "链表创建完成,共" << size << "个节点!" << endl;
}
插入规则:
// 插入节点:在第pos个位置插入值为val的节点(pos从1开始)
bool SinglyLinkedList::insertNode(int pos, int val) {
// 检查位置合法性
if (pos < 1 || pos > size + 1) {
cout << "插入位置非法!" << endl;
return false;
}
// 新建节点
ListNode* newNode = new ListNode(val);
// 情况1:插入到表头(pos=1)
if (pos == 1) {
newNode->next = head;
head = newNode;
}
// 情况2:插入到表中/表尾
else {
ListNode* prev = head;
// 找到第pos-1个节点(前驱节点)
for (int i = 1; i < pos - 1; i++) {
prev = prev->next;
}
newNode->next = prev->next;
prev->next = newNode;
}
size++;
cout << "节点" << val << "插入到位置" << pos << "成功!" << endl;
return true;
}
删除规则:
// 删除第pos个位置的节点(pos从1开始)
bool SinglyLinkedList::deleteNode(int pos) {
// 检查位置合法性
if (pos < 1 || pos > size) {
cout << "删除位置非法!" << endl;
return false;
}
ListNode* delNode = nullptr;
// 情况1:删除表头节点(pos=1)
if (pos == 1) {
delNode = head;
head = head->next;
}
// 情况2:删除表中/表尾节点
else {
ListNode* prev = head;
// 找到第pos-1个节点(前驱节点)
for (int i = 1; i < pos - 1; i++) {
prev = prev->next;
}
delNode = prev->next;
prev->next = delNode->next;
}
// 释放删除节点的内存
int delVal = delNode->data;
delete delNode;
delNode = nullptr;
size--;
cout << "位置" << pos << "的节点" << delVal << "删除成功!" << endl;
return true;
}
从表头开始,逐个访问节点数据,直到链表末尾:
// 遍历链表并输出所有节点数据
void SinglyLinkedList::traverseList() {
if (head == nullptr) {
cout << "链表为空!" << endl;
return;
}
cout << "链表节点:";
ListNode* curr = head;
while (curr != nullptr) {
cout << curr->data << " ";
curr = curr->next;
}
cout << endl;
}
按值查找,返回第一个匹配值的节点指针(未找到返回nullptr):
// 查找值为val的节点,返回节点指针(未找到返回nullptr)
ListNode* SinglyLinkedList::findNode(int val) {
ListNode* curr = head;
while (curr != nullptr) {
if (curr->data == val) {
cout << "找到节点" << val << "!" << endl;
return curr;
}
curr = curr->next;
}
cout << "未找到节点" << val << "!" << endl;
return nullptr;
}
// 测试单链表
int main() {
SinglyLinkedList list;
// 创建链表
int arr[] = {10, 20, 30, 40, 50};
list.createList(arr, 5);
list.traverseList(); // 输出:10 20 30 40 50
// 插入节点
list.insertNode(3, 25);
list.traverseList(); // 输出:10 20 25 30 40 50
// 删除节点
list.deleteNode(5);
list.traverseList(); // 输出:10 20 25 30 50
// 查找节点
list.findNode(25); // 找到节点25
list.findNode(60); // 未找到节点60
cout << "链表长度:" << list.getSize() << endl; // 输出:5
return 0;
}
双向链表(Doubly Linked List)每个节点包含数据域、前驱指针域(指向前驱节点)和后继指针域(指向后继节点)。表头节点的前驱指针为nullptr,表尾节点的后继指针为nullptr。
双向链表的特点:
#include <iostream>
#include <cstdlib>
using namespace std;
// 定义双向链表节点结构体
struct DListNode {
int data; // 数据域
DListNode* prev; // 前驱指针:指向前一个节点
DListNode* next; // 后继指针:指向后一个节点
// 构造函数
DListNode(int val = 0) : data(val), prev(nullptr), next(nullptr) {}
};
// 定义双向链表类
class DoublyLinkedList {
private:
DListNode* head; // 头指针
DListNode* tail; // 尾指针(优化尾插/尾删效率)
int size; // 节点个数
public:
// 构造函数
DoublyLinkedList() : head(nullptr), tail(nullptr), size(0) {}
// 析构函数
~DoublyLinkedList() {
DListNode* curr = head;
while (curr != nullptr) {
DListNode* temp = curr;
curr = curr->next;
delete temp;
}
head = tail = nullptr;
size = 0;
}
// 声明核心操作函数
void initList(); // 初始化
void createList(int arr[], int n); // 创建链表
bool insertNode(int pos, int val); // 插入
bool deleteNode(int pos); // 删除
DListNode* findNode(int val); // 查找
void traverseForward(); // 正向遍历
void traverseBackward(); // 反向遍历
int getSize() { return size; }
};
// 初始化空链表
void DoublyLinkedList::initList() {
if (head != nullptr) {
DListNode* curr = head;
while (curr != nullptr) {
DListNode* temp = curr;
curr = curr->next;
delete temp;
}
}
head = tail = nullptr;
size = 0;
cout << "双向链表初始化完成!" << endl;
}
// 从数组创建双向链表(尾插法)
void DoublyLinkedList::createList(int arr[], int n) {
initList();
if (n <= 0) return;
// 创建第一个节点
head = new DListNode(arr[0]);
tail = head;
size = 1;
// 逐个插入后续节点
for (int i = 1; i < n; i++) {
DListNode* newNode = new DListNode(arr[i]);
tail->next = newNode; // 尾节点后继指向新节点
newNode->prev = tail; // 新节点前驱指向尾节点
tail = newNode; // 尾指针后移
size++;
}
cout << "双向链表创建完成,共" << size << "个节点!" << endl;
}
// 在第pos个位置插入值为val的节点(pos从1开始)
bool DoublyLinkedList::insertNode(int pos, int val) {
if (pos < 1 || pos > size + 1) {
cout << "插入位置非法!" << endl;
return false;
}
DListNode* newNode = new DListNode(val);
// 情况1:插入到表头
if (pos == 1) {
if (head == nullptr) { // 空链表
head = tail = newNode;
} else {
newNode->next = head;
head->prev = newNode;
head = newNode;
}
}
// 情况2:插入到表尾
else if (pos == size + 1) {
tail->next = newNode;
newNode->prev = tail;
tail = newNode;
}
// 情况3:插入到表中
else {
DListNode* curr = head;
// 找到第pos个节点
for (int i = 1; i < pos; i++) {
curr = curr->next;
}
// 修改指针
newNode->prev = curr->prev;
newNode->next = curr;
curr->prev->next = newNode;
curr->prev = newNode;
}
size++;
cout << "节点" << val << "插入到位置" << pos << "成功!" << endl;
return true;
}
// 删除第pos个位置的节点(pos从1开始)
bool DoublyLinkedList::deleteNode(int pos) {
if (pos < 1 || pos > size) {
cout << "删除位置非法!" << endl;
return false;
}
DListNode* delNode = nullptr;
// 情况1:删除表头节点
if (pos == 1) {
delNode = head;
head = head->next;
if (head != nullptr) {
head->prev = nullptr;
} else { // 链表只剩一个节点
tail = nullptr;
}
}
// 情况2:删除表尾节点
else if (pos == size) {
delNode = tail;
tail = tail->prev;
tail->next = nullptr;
}
// 情况3:删除表中节点
else {
delNode = head;
for (int i = 1; i < pos; i++) {
delNode = delNode->next;
}
delNode->prev->next = delNode->next;
delNode->next->prev = delNode->prev;
}
// 释放内存
int delVal = delNode->data;
delete delNode;
delNode = nullptr;
size--;
cout << "位置" << pos << "的节点" << delVal << "删除成功!" << endl;
return true;
}
// 正向遍历(表头到表尾)
void DoublyLinkedList::traverseForward() {
if (head == nullptr) {
cout << "双向链表为空!" << endl;
return;
}
cout << "正向遍历:";
DListNode* curr = head;
while (curr != nullptr) {
cout << curr->data << " ";
curr = curr->next;
}
cout << endl;
}
// 反向遍历(表尾到表头)
void DoublyLinkedList::traverseBackward() {
if (tail == nullptr) {
cout << "双向链表为空!" << endl;
return;
}
cout << "反向遍历:";
DListNode* curr = tail;
while (curr != nullptr) {
cout << curr->data << " ";
curr = curr->prev;
}
cout << endl;
}
// 查找值为val的节点(正向查找)
DListNode* DoublyLinkedList::findNode(int val) {
DListNode* curr = head;
while (curr != nullptr) {
if (curr->data == val) {
cout << "找到节点" << val << "!" << endl;
return curr;
}
curr = curr->next;
}
cout << "未找到节点" << val << "!" << endl;
return nullptr;
}
// 测试双向链表
int main() {
DoublyLinkedList dlist;
// 创建链表
int arr[] = {1, 2, 3, 4, 5};
dlist.createList(arr, 5);
dlist.traverseForward(); // 输出:1 2 3 4 5
dlist.traverseBackward(); // 输出:5 4 3 2 1
// 插入节点
dlist.insertNode(3, 8);
dlist.traverseForward(); // 输出:1 2 8 3 4 5
// 删除节点
dlist.deleteNode(4);
dlist.traverseForward(); // 输出:1 2 8 4 5
// 查找节点
dlist.findNode(8); // 找到节点8
dlist.findNode(9); // 未找到节点9
cout << "链表长度:" << dlist.getSize() << endl; // 输出:5
return 0;
}
循环链表是单链表的变种,表尾节点的后继指针不指向nullptr,而是指向表头节点(单循环链表)或头节点(带哨兵头节点的循环链表),形成闭合的环。
循环链表的特点:
本教程以单循环链表为例讲解。
#include <iostream>
#include <cstdlib>
using namespace std;
// 定义循环链表节点结构体
struct CListNode {
int data; // 数据域
CListNode* next; // 后继指针
// 构造函数
CListNode(int val = 0) : data(val), next(nullptr) {}
};
// 定义循环链表类
class CircularLinkedList {
private:
CListNode* head; // 头指针
int size; // 节点个数
public:
// 构造函数
CircularLinkedList() : head(nullptr), size(0) {}
// 析构函数
~CircularLinkedList() {
if (head == nullptr) return;
CListNode* curr = head->next;
// 遍历并释放所有节点
while (curr != head) {
CListNode* temp = curr;
curr = curr->next;
delete temp;
}
delete head;
head = nullptr;
size = 0;
}
// 声明核心操作函数
void initList(); // 初始化
void createList(int arr[], int n); // 创建链表
bool insertNode(int pos, int val); // 插入
bool deleteNode(int pos); // 删除
CListNode* findNode(int val); // 查找
void traverseList(); // 遍历
int getSize() { return size; }
};
// 初始化空链表
void CircularLinkedList::initList() {
if (head != nullptr) {
// 释放循环链表所有节点
CListNode* curr = head->next;
while (curr != head) {
CListNode* temp = curr;
curr = curr->next;
delete temp;
}
delete head;
}
head = nullptr;
size = 0;
cout << "循环链表初始化完成!" << endl;
}
// 从数组创建循环链表(尾插法)
void CircularLinkedList::createList(int arr[], int n) {
initList();
if (n <= 0) return;
// 创建第一个节点
head = new CListNode(arr[0]);
CListNode* tail = head;
size = 1;
// 插入后续节点
for (int i = 1; i < n; i++) {
CListNode* newNode = new CListNode(arr[i]);
tail->next = newNode;
tail = newNode;
size++;
}
// 尾节点指向头节点,形成循环
tail->next = head;
cout << "循环链表创建完成,共" << size << "个节点!" << endl;
}
// 在第pos个位置插入值为val的节点(pos从1开始)
bool CircularLinkedList::insertNode(int pos, int val) {
if (pos < 1 || pos > size + 1) {
cout << "插入位置非法!" << endl;
return false;
}
CListNode* newNode = new CListNode(val);
// 情况1:空链表插入第一个节点
if (head == nullptr) {
head = newNode;
newNode->next = head;
}
// 情况2:插入到表头(pos=1)
else if (pos == 1) {
// 找到尾节点
CListNode* tail = head;
while (tail->next != head) {
tail = tail->next;
}
newNode->next = head;
tail->next = newNode;
head = newNode; // 头指针指向新节点
}
// 情况3:插入到表中/表尾
else {
CListNode* prev = head;
// 找到第pos-1个节点
for (int i = 1; i < pos - 1; i++) {
prev = prev->next;
}
newNode->next = prev->next;
prev->next = newNode;
// 若插入到表尾,更新尾节点(可选,本实现未维护尾指针)
if (pos == size + 1) {
newNode->next = head;
}
}
size++;
cout << "节点" << val << "插入到位置" << pos << "成功!" << endl;
return true;
}
// 删除第pos个位置的节点(pos从1开始)
bool CircularLinkedList::deleteNode(int pos) {
if (pos < 1 || pos > size) {
cout << "删除位置非法!" << endl;
return false;
}
CListNode* delNode = nullptr;
CListNode* tail = nullptr;
// 情况1:链表只有一个节点
if (size == 1) {
delNode = head;
head = nullptr;
}
// 情况2:删除表头节点
else if (pos == 1) {
// 找到尾节点
tail = head;
while (tail->next != head) {
tail = tail->next;
}
delNode = head;
head = head->next;
tail->next = head; // 尾节点指向新表头
}
// 情况3:删除表中/表尾节点
else {
CListNode* prev = head;
// 找到第pos-1个节点
for (int i = 1; i < pos - 1; i++) {
prev = prev->next;
}
delNode = prev->next;
prev->next = delNode->next;
// 若删除表尾节点,确保尾节点指向表头
if (delNode->next == head && pos == size) {
prev->next = head;
}
}
// 释放内存
int delVal = delNode->data;
delete delNode;
delNode = nullptr;
size--;
cout << "位置" << pos << "的节点" << delVal << "删除成功!" << endl;
return true;
}
遍历循环链表需注意终止条件(回到头节点),避免无限循环:
// 遍历循环链表
void CircularLinkedList::traverseList() {
if (head == nullptr) {
cout << "循环链表为空!" << endl;
return;
}
cout << "循环链表节点:";
CListNode* curr = head;
do {
cout << curr->data << " ";
curr = curr->next;
} while (curr != head); // 终止条件:回到头节点
cout << endl;
}
// 查找值为val的节点
CListNode* CircularLinkedList::findNode(int val) {
if (head == nullptr) {
cout << "循环链表为空!" << endl;
return nullptr;
}
CListNode* curr = head;
do {
if (curr->data == val) {
cout << "找到节点" << val << "!" << endl;
return curr;
}
curr = curr->next;
} while (curr != head);
cout << "未找到节点" << val << "!" << endl;
return nullptr;
}
// 测试循环链表
int main() {
CircularLinkedList clist;
// 创建链表
int arr[] = {100, 200, 300, 400, 500};
clist.createList(arr, 5);
clist.traverseList(); // 输出:100 200 300 400 500
// 插入节点
clist.insertNode(2, 150);
clist.traverseList(); // 输出:100 150 200 300 400 500
// 删除节点
clist.deleteNode(5);
clist.traverseList(); // 输出:100 150 200 300 500
// 查找节点
clist.findNode(300); // 找到节点300
clist.findNode(600); // 未找到节点600
cout << "链表长度:" << clist.getSize() << endl; // 输出:5
return 0;
}
nullptr,避免程序崩溃;nullptr;nullptr,避免野指针访问。掌握链表的实现与操作,是理解更复杂数据结构(如链表版栈、队列、哈希表)的基础,也是C++数据结构学习的核心内容之一。